National Park Service
U.S. Department of the Interior

Natural Resource Stewardship and Science

National Park Service logo

1 Background & Introduction

The following information is summarized from the Springs in the Mojave Desert Network—Surface water monitoring at desert springs: Protocol narrative version 1.0 (NPS/MOJN/NRR—2018/1718), available here: https://irma.nps.gov/DataStore/Reference/Profile/2254900. Refer to the protocol narrative for more detailed information.

1.1 Significance

Springs are water sources that form where groundwater reaches the land surface. They can range in size from seasonal seeps that go dry in the summer to perennial streams that flow all year long and support large wetland environments (Figure 1). Compared to the surrounding desert, spring and wetland environments in the Mojave and Great Basin regions support a greater abundance and diversity of plant and animal life. Large mammals, like desert bighorn sheep, depend on standing water to survive the heat of summer. Many birds depend on riparian habitat during their migratory seasons, and bats frequent open pools when foraging for insects. Some springs support rare or endemic species, including fish, amphibians, and aquatic invertebrates. Reliable water sources are scarce, so springs are often vital to the distribution and connectivity of wildlife habitat across the landscape.

Figure 1. Examples of springs in the Mojave Desert.

1.2 Threats and Stressors

Springs are vulnerable to several stressors, including climate change and groundwater withdrawal. In the southwestern United States, increasing air temperatures and declining snowpacks have intensified drought and reduced aquifer recharge (USGCRP 2018). Springs fed by small, local aquifers are particularly vulnerable to drought. They may experience lower discharge or even go dry entirely. Springs fed by larger, regional aquifers are more resilient to recent climate change but may be vulnerable to groundwater withdrawal happening outside of their park. For large aquifers with slow recharge, it may take centuries for water levels to recover after pumping is discontinued (Bredehoeft and Durbin 2009). At all springs, higher air temperatures can lead to water loss through increases in evapotranspiration as well as changes in plant communities and wildlife use patterns.

Other stressors include surface water diversions, livestock disturbance, recreation, and invasive species. Active and historical diversions, such as pipes and troughs, are widespread at springs within the network and may alter or eliminate downstream riparian habitat. Grazing and trampling by livestock, such as cattle, feral donkeys (burro), and feral horses, can also negatively affect riparian vegetation and habitat. Threats from improper recreation include social trails, fire pits, bank degradation, and failure to dispose of garbage and human waste. Invasive trees like salt cedar and palms often outcompete native plants for limited water and nutrients, and invasive grasses are shifting fire regimes across the landscape. Invasive animals, including amphibians, fish, arthropods, and molluscs, can put pressure on native species through predation, hybridization, or competition for resources.

1.3 Sample Design

The Mojave Desert Network Inventory and Monitoring Program (MOJN I&M) monitors 248 springs across Death Valley National Park (DEVA), Joshua Tree National Park (JOTR), Lake Mead National Recreation Area (LAKE), Mojave National Preserve (MOJA), and Grand Canyon-Parashant National Monument (PARA) as part of the Desert Springs protocol (Figure 2). Springs were selected at each park using a Generalized Random Tessellation Stratified (GRTS) spatially-balanced random sample.

A subset of 60 springs are monitored annually and are equipped with data-logging sensors to provide continuous records of surface water presence. There are 20 annual springs in DEVA and 10 annual springs in the other four parks. The remaining 188 springs are monitored on a 3-year rotation. There are 60 3-year springs in DEVA, 35 in MOJA, 35 in PARA, 33 in LAKE, and 25 in JOTR. Approximately 120 springs are monitored each field season.

Figure 2. Desert springs monitoring locations. Blue circles indicate springs monitored every year. Red circles indicate springs monitored every three years.

1.4 Monitoring Questions

Given the ecological importance of springs and the threats that they face, the Desert Springs protocol was developed to address the following monitoring questions:

  • Is water availability at springs changing over time?
  • Is water quality at springs changing over time?

The protocol investigates these monitoring questions by collecting long-term data for the following quantitative measurable objectives:

  • Status and trends of water availability (flow condition, discharge, and surface water length).
  • Status and trends of water quality (temperature, pH, specific conductance, and dissolved oxygen).

In addition to these measurable objectives, the protocol also collects qualitative site condition information, including:

  • Dominant vegetation
  • Invasive plants
  • Disturbance
  • Wildlife evidence
  • Repeat Photographs

Because MOJN I&M field crews are often the only NPS field crews visiting many of these springs regularly, this site condition information can provide park managers with useful information about springs that would not be collected otherwise.

2 Methods

2.1 Data Collection

Field methods used are described in Springs in the Mojave Desert Network—Surface water monitoring at desert springs: Standard operating procedures version 1.0, available here: https://irma.nps.gov/DataStore/Reference/Profile/2256063. Specific methods include SOP 4: Spring Acceptance and Classification, SOP 5: Water Availability: Flow Condition, SOP 6: Water Availability: Data-Logging Sensors, SOP 7: Water Quality, SOP 8: Site Condition: Spring Vegetation, SOP 9: Site Condition: Invasive Plants, SOP 10: Site Condition: Disturbance, and SOP 11: Site Condition: Repeat Photographs. Table 1 summarizes all measurements that are collected for the protocol.

Table 1. Monitoring data collected for the Desert Springs protocol.

2.2 Data Processing

Data were evaluated using a collection of QC checks that are part of the MOJN I&M Streams and Lakes R package. These evaluations are summarized in Section 4 of this document. Where unresolvable data quality issues existed, they have been described by a flag note in the data record, and a flag has been assigned to indicate the level of concern. These flags include Information (not expected to impact the validity of this data), Warning (care should be taken when including this record in analyses), and Critical (serious concerns about the quality of this record and it should not be used in analyses).

2.3 Code Availability

All of the code used in the preparation of this data package is available in the MOJN I&M Desert Springs R Package .

3 Data Records

CSV data files are provided that contain raw values suitable for use with the MOJN I&M Desert Springs R package. additional CSV data files are provided that contain calculated values generated using this R package. These files are clearly labeled as “calculated” (Table 1).

A data dictionary for all of the tables and fields in the published CSV files is provided in XML format with this data package.

The published product is one data package containing multiple CSV data files:

  • MOJN I&M Desert Springs Data Package 2016-2022. Comma-separated text files containing data from the MOJN I&M Desert Springs protocol. These data were compiled by the MOJN I&M and were last updated on . Available at https://irma.nps.gov/DataStore/Reference/Profile/.

4 Data Quality Evaluation

The data within the data records listed above have been reviewed by staff in the NPS Inventory and Monitoring Division to ensure accuracy, completeness, and consistency with documented data quality standards, as well as for usability and reproducibility. For additional information on data quality standards for this protocol, refer to Data Quality Standards for Springs in the Mojave Desert Network–Surface Water Monitoring at Desert Springs (NPS/MOJN/NRR—2018/1819), available here: https://irma.nps.gov/DataStore/Reference/Profile/2257548.

4.1 Advice and Caveats

  • During the first field season in WY2016, LAKE springs were monitored in March and April. In all subsequent field seasons, LAKE springs were monitored in November and December.
  • CAMO was established in February 2016. It was inventoried in November 2016, and two possible springs were identified. These two springs are monitored opportunistically every 3-years at the same time as the MOJA 3-year springs.
  • Due to the 35-day lapse in federal appropriations from late December 2018 to late January 2019, one annual spring in DEVA and both springs in CAMO were not monitored during WY2019.
  • Due to COVID-19 pandemic travel restrictions beginning in March 2020, monitoring of annual and 3-year springs was not completed in PARA during WY2020.
  • Due to COVID-19 pandemic travel restrictions from December 2020 to February 2021, monitoring of 3-year springs was not completed in DEVA during WY2021.
  • In WY2016, water quality instruments were not maintained to protocol standards. In particular, dissolved oxygen and pH measurements from the first field season may not be high quality data.
  • In WY2018, calibration and measurement of dissolved oxygen percent began using the local dissolved oxygen percent convention. Dissolved oxygen percent may not be comparable between years before and after this change.
  • In WY2019, ungulate observations were separated from mammal observations in wildlife evidence.
  • In WY2021, measurement of springbrook length was separated into continuous and discontinuous measurements. Previously, only continuous springbrook length was measured.

4.2 General

4.2.1 Not sampled

This table lists springs that were not sampled during a field season or visit when they were intended to be monitored. No spring found indicates that there was no evidence of recent discharge, even periodic. Evidence could include riparian or denser wash vegetation or channelization. No spring found usually leads to a spring being rejected and removed from the sample frame. Inaccessible indicates that there were temporary or permanent barriers to physically reaching a spring. Barriers could include road washouts, unsafe terrain, or private property. Some springs were deemed inaccessible due to unsafe terrain in the office, while other springs were deemed inaccessible upon observation of unsafe terrain in the field. Inaccessible springs are skipped in the GRTS draw order, but they are not rejected or removed from the sample frame.

4.2.2 Completeness

This table provides the number and percentage of springs monitored at each park for each field season. Large disruptions to the field season occurred in WY2019 with the 35-day lapse in federal appropriations and in WY2020 and WY2021 with travel restrictions due to the COVID-19 pandemic.

4.2.3 Completeness plot

This stacked bar plot shows the percentage of springs monitored at each park for each field season. Large disruptions to the field season occurred in WY2019 with the 35-day lapse in federal appropriations and in WY2020 and WY2021 with travel restrictions due to the COVID-19 pandemic.

4.2.4 Visit dates

This table provides the date (month and day) that each spring was visited during each field season. Springs should be visited at approximately the same time of year during each field season, ideally within a few weeks, in order to minimize seasonal effects on the data.

4.2.5 Visit dates timeline

These timelines show the date (month and day) that each spring was visited during each field season. They also show the median visit date across all field seasons at each spring. Springs should be visited at approximately the same time of year during each field season, ideally within a few weeks, in order to minimize seasonal effects on the data.

4.2.6 Repeat visits

This table provides a list of springs that were visited multiple times during a field season. There should only be one primary visit per field season. Supplemental visits involve collecting specific data to supplement the data collected during the primary visit. Supplemental visits typically occur when a sensor is inadvertently forgotten during the primary visit and the spring is revisited later in the field season for sensor deployment. Replicate visits involve collecting a full set of data in addition to the data collected during the primary visit. Replicate visits may occur during training or when calibrating or comparing field crews.

5 {r repeats} # repeats <- qcRepeatVisits(conn = conn, data.source = "database") # # if (params$isAccessible == "yes") { # knitr::kable(repeats) %>% # kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left") # } else { # reactable(dplyr::select(repeats, -Notes), # defaultColDef = colDef(minWidth = 50), # searchable = TRUE, # details = colDef( # name = "Notes", # details = function(index) { # note <- repeats$Notes[index] # if (!is.na(note)) { # return(note) # } # } # ), # resizable = TRUE, # compact = TRUE, # striped = TRUE) # } #

5.0.1 Spring type discrepancies

This table provides a list of springs that have been categorized differently (e.g., rheocrene vs hillslope) during different visits. Discrepancies typically occur when a spring is dry, and it is difficult to determine the spring type, or when a spring exhibits multiple spring types along its reach.

5.0.2 Data processing levels

This table lists site visits that have any data labeled as “Raw” or “Provisional.” These data need to be reviewed and moved to “Accepted” status before publication of the dataset.

The above records need to be reviewed and accepted before publication.

5.1 Sensor information

5.1.1 Sensor summary

This table summarizes sensor deployment, retrieval, and successful download status for each park and field season. Percent retrieved is the percentage of sensors that were successfully recovered of those where a retrieval attempt was made. Percent downloaded is the percentage of sensors that were successfully downloaded of those recovered. Beginning in 2019, a new sensor model was deployed that raised the percent downloaded success rate to 100%.

5.1.2 Sensor recovery heatmap

This heatmap shows which sites have consistent sensor recovery, sensor loss, or a combination of both. Gaps indicate that a sensor was not deployed during that field season.

5.1.3 Sensors not deployed

This table lists annual springs where a sensor was not deployed. If there is a visit date associated with the field season, then the spring was visited, but a sensor was not deployed during the visit. If there is no visit date associated with the field season, then the spring was not visited during that field season.

6 {r nodeployment} # nodata <- qcSensorsNotDeployed(conn, data.source = "database") # # if (params$isAccessible == "yes") { # knitr::kable(nodata) %>% # kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left") # } else { # nodata %>% # reactable(searchable = TRUE, compact = TRUE, striped = TRUE) # } #

6.0.1 Sensors not recovered

This table lists springs where a sensor previously deployed was not recovered. If there is a visit date associated with the field season, then the spring was visited, but a sensor was not recovered during the visit. If there is no visit date associated with the field season, then the spring was not visited during that field season.

7 {r noretrieval} # nodata <- qcSensorsNotRecovered(conn, data.source = "database") # # if (params$isAccessible == "yes") { # knitr::kable(nodata) %>% # kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left") # } else { # reactable(dplyr::select(nodata, -Notes), # defaultColDef = colDef(minWidth = 50), # searchable = TRUE, # details = colDef( # name = "Notes", # details = function(index) { # note <- nodata$Notes[index] # if (!is.na(note)) { # return(note) # } # } # ), # resizable = TRUE, # compact = TRUE, # striped = TRUE) # } #

7.0.1 Problems with recovered sensors

This table lists the sensors that were recovered with problems. Damaged sensors were recovered in the correct location, but showed obvious damage or tampering. Buried sensors were recovered in the correct location, but found buried under substrate, which could affect their ability to detect surface water presence. Outside water sensors were recovered in the correct location, but found laying outside of nearby surface water. This can happen when sensors are deployed when the spring is dry and the source has to be estimated, or when the source shifts between visits. Wrong location sensors were recovered but not from where they were originally installed. This can happen when flooding during heavy precipitation pulls bolts out of the substrate and washes sensors down stream.

7.0.2 Sensors both missing and downloaded

This table lists sensors that were successfully downloaded but also recorded as missing. This situation can happen when a sensor is pulled into a rodent burrow or buried by sediment and is not able to be located, but is still near enough to connect via Bluetooth.

7.0.3 Sensors recovered, download status unknown

This table lists sensors that were recovered, but there is no data if they were successfully downloaded or not.

7.0.4 Retrieval date same as deployment date

This table lists sensors that have the same retrieval and deployment dates. This may indicate an error during data entry.

7.0.5 Unknown sensor IDs

This table lists sensors whose sensor ID or serial number are unknown.

8 {r unknownsensorids} # unkids <- qcUnknownSensorIDs(conn, data.source = "database") # # if (params$isAccessible == "yes") { # knitr::kable(unkids) %>% # kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left") # } else { # reactable(dplyr::select(unkids, -Notes), # defaultColDef = colDef(minWidth = 50), # searchable = TRUE, # details = colDef( # name = "Notes", # details = function(index) { # note <- unkids$Notes[index] # if (!is.na(note)) { # return(note) # } # } # ), # resizable = TRUE, # compact = TRUE, # striped = TRUE) # } #

8.1 Flow condition and water quantity

Water quantity is the master variable in spring environments. The amount, timing, and duration of discharge control the character and extent of different aquatic habitats, the abundance of wetland plants that can be supported, and the ability of wildlife to access water for drinking.

8.1.1 Spring discharge summary

This table provides spring flow condition and either estimated or volumetric discharge for each visit.

8.1.2 Spring dry, discharge or springbrook > 0

This table lists visits where the spring was recorded as dry, but either discharge or springbrook length were recorded as greater than 0.

8.1.3 Spring not dry, discharge or springbrook = 0

This table lists visits where the spring was not recorded as dry and discharge was recorded as 0 or where the spring was not recorded as dry or wet soil only and springbrook length was recorded as 0. The former situation can arise when wet soil is believed to result from recent rainfall or snowmelt as opposed to groundwater discharge. The latter condition can arise when there is water dripping from a pipe that is not enough to saturate the soil below.

8.1.4 Discharge missing

This table lists visits where estimated or volumetric discharge were not recorded.

8.1.5 Volumetric method data missing

This table lists visits where the volumetric method was used, but container volume, percent of flow, or fill time data were not recorded. Missing any one of these data types makes it impossible to calculate discharge.

8.1.6 Volumetric method fills < 5

This table lists visits where the volumetric method was used, but there are fewer than 5 fill times. Typically, at least 5 fill times are necessary to reduce the effect of outliers on determining the median fill time.

8.1.7 Volumetric method median fill seconds < 5

This table lists visits where the volumetric method was used, but the median fill time was less than 5 seconds. Typically, fill times should be greater than 5 seconds to provide sufficient accuracy and precision.

8.1.8 Continuous > discontinuous

This table lists visits where continuous surface water length is greater than discontinuous surface water length. This results from error during data entry.

8.1.9 Continuous flow category summary

This table summaries flow categories for the continuous portion of springbrooks.

8.1.10 Discontinuous flow category summary

This table summaries flow categories for the discontinuous portion of springbrooks, where springbrook lengths are discontinuous, and the continuous portion of springbrooks, where springbrook lengths are continuous.

8.1.11 Flow categories annual plot

This bar plot summarizes flow categories for annual springs.

8.1.12 Flow categories three-year plot

This bar plot summarizes flow categories for 3-year springs.

8.1.13 Flow categories annual heatmap

This heatmap shows flow categories at each annual spring over all field seasons.

  Warning in ReadAndFilterData(conn = conn, path.to.data = path.to.data, park =
  park, : DischargeVolumetric: Data are not available for the park specified

8.1.14 Flow categories three-year heatmap

This heatmap shows flow categories at each three year spring over all field seasons.

  Warning in ReadAndFilterData(conn = conn, path.to.data = path.to.data, park =
  park, : DischargeVolumetric: Data are not available for the park specified

8.1.15 Flow category map

This map shows flow categories at annual and three year springs. If there is a field season slider at the top of the map, then data from a specific field season may be selected. If there is no field season slider, then data from the most recent field season are shown.

8.2 Water quality

Water quality includes four parameters: water temperature, specific conductance, pH, and dissolved oxygen. Water temperature influences many chemical and physical properties of water as well as the metabolic activity of aquatic organisms. Specific conductance is related to salinity and measures the concentration of dissolved ions in the water, which can help detect contamination or disturbance. pH affects the solubility and toxicity of chemicals and heavy metals in the water. Dissolved oxygen sustains aquatic organisms that breathe aerobically, including crustaceans, mollusks, insects, amphibians, and fish.

8.2.1 Water quality sanity

These are water quality values that fall above or below the ranges that we typically see in spring systems. These data are not necessarily incorrect, but they are outliers that should be evaluated using data quality flags and field notes. Wildly impossible values may be the result of instrument malfunction, improper calibration, or typos during data entry. The following records are included in the list below: temperature values greater than 30 C, pH values greater than 10 and less than 6, specific conductance values greater than 20000 uS/cm, dissolved oxygen percent values greater than 110% or less than 2%, and dissolved oxygen concentration values greater than 12 mg/L.

8.2.2 Water quality flags

These are water quality values that have data quality flags. I = Information: These data do not have any suspected problems, but there may be information regarding the equipment or conditions in which they were collected that could inform their interpretation. W = Warning: These data are suspected to have problems and should only be used after careful assessment of instrument and environmental factors. C = Critical: These data are suspected to have serious problems and are likely unusable.

8.2.3 Local dissolved oxygen calibration

This table lists visits where dissolved oxygen percent was not calibrated to local dissolved oxygen. When calibrating to local dissolved oxygen, the calibration value is 100% regardless of the barometric pressure at the time of calibration. The 100% calibration value for this convention reflects the fact that the calibration environment is at 100% oxygen pressure for that specific location.

9 {r docalcheck} # localdo <- qcLocalDOCheck(conn, data.source = "database") # # if (params$isAccessible == "yes") { # knitr::kable(localdo) %>% # kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left") # } else { # localdo %>% # reactable(searchable = TRUE, compact = TRUE, striped = TRUE) # } #

9.0.1 Specific conductance standards

This table lists visits where the specific conductance standard used during calibration was much lower than the in-situ reading of specific conductance at the spring. Ideally, specific conductance standards of 10,000 uS/cm or even 50,000 uS/cm should be used at springs with consistently higher specific conductance readings.

10 {r spcondstandardcheck} # scstandard <- qcSpCondStandardCheck(conn, data.source = "database") # # if (params$isAccessible == "yes") { # knitr::kable(scstandard) %>% # kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left") # } else { # scstandard %>% # reactable(searchable = TRUE, compact = TRUE, striped = TRUE) # } #

10.0.1 Water quality summary statistics

This table summarizes the median and quartile values for each water quality parameter for each park and field season. Data flagged as “Warning” or “Critical” are not included.

10.0.2 Water temperature box plot

These box plots show the median and quartile values for water temperature (C) for each park and year. Data flagged as “Warning” or “Critical” are not included.

10.0.3 pH box plot

These box plots show the median and quartile values for pH for each park and year. Data flagged as “Warning” or “Critical” are not included.

10.0.4 Specific conductance box plot

These box plots show the median and quartile values for specific conductance (uS/cm) for each park and year. Data flagged as “Warning” or “Critical” are not included.

10.0.5 Dissolved oxygen box plot

These box plots show the median and quartile values for dissolved oxygen concentration (mg/L) for each park and year. Data flagged as “Warning” or “Critical” are not included.

10.0.6 Temperature map

Not yet implemented…

10.0.7 Specific conductance map

Not yet implemented…

10.0.8 pH map

Not yet implemented…

10.0.9 Dissolved oxygen map

Not yet implemented…

10.1 Riparian vegetation

10.1.1 Vegetation present, no lifeforms

This table lists visits where vegetation was recorded as present, but no lifeforms were documented.

10.1.2 No vegetation observed, lifeforms present

This table lists visits where vegetation was not recorded as present, but lifeforms were documented.

10.1.3 Lifeform present, no rank

This table lists visits where one or more lifeforms are missing a rank.

10.1.4 Lifeform rank check

This table lists visits where multiple life forms have the same rank, and rank gaps have not been properly entered.

10.1.5 Lifeform observations

This table counts the number of springs where each lifeform was observed in each park and field season.

10.1.6 Lifeforms per spring plot

This bar plot shows the number of different lifeforms that were observed at springs in each park across all field seasons. The black vertical line indicates the median number of different lifeforms at springs in each park across all field seasons, while the red dashed line indicates the mean number of different lifeforms at springs in each park across all field seasons.

  Warning: `gather_()` was deprecated in tidyr 1.2.0.
  Please use `gather()` instead.
  This warning is displayed once every 8 hours.
  Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.

10.1.7 Most common lifeforms plot

This bar plot shows the number of observations of each lifeform in each park across all field seasons. Lifeforms are sorted by most common to least common in each park.

10.2 Invasive plants

10.2.1 Targeted invasive plants presence

This table lists visits where targeted invasive plants were observed. Targeted invasive plants include saltcedar (Tamarix ramosissima), California fan palm (Washingtonia filifera), data palm (Phoenix dactylifera), and fountain grass (Pennisetum setaceum). California fans palms are native to Joshua Tree National Park, so any observations there are not included in this table.

10.2.2 Targeted invasive plants map

This map shows observations of targeted invasive plant species. If there is a field season slider at the top of the map, then data from a specific field season may be selected. If there is no field season slider, then data from the most recent field season are shown.

10.3 Flow modification and disturbance

10.3.1 Overall disturbance < any other disturbance category

This table lists visits where overall disturbance was less than any other disturbance category. This indicates error during data entry.

10.3.2 Flow modification exists, no Human Use disturbance recorded

This table lists visits were flow modification exists, but no human use disturbance was recorded at the spring.

10.3.3 Flow modification discrepancies

This table provides a list of springs where different flow modification types have been recorded during different visits. This may happen if a flow modification type is overlooked in one or more field seasons, a spring source shifts, or a different area around the spring source is searched during a visit.

10.3.4 Springs with active or historic flow modification

This table lists springs where active or inactive (historical) flow modification was observed in each field season.

10.3.5 Flow modification summary

This table lists the number and percentage of springs where active, inactive (historical), or no flow modification have been observed. In situations where there are discrepancies in flow modification observations across different field seasons, the most active disturbance is included.

10.3.6 Flow modification bar plot

This bar plot shows the percentage of springs in each park where active, inactive (historical), or no flow modification have observed. In situations where there are discrepancies in flow modification observations across different field seasons, the most active disturbance is included.

10.3.7 Disturbance summary

This table lists the number and percentage of springs where human use or livestock disturbance have been observed. In situations where disturbance is not observed in all years, ___________________.

10.3.8 Human use presence

This table lists visits where human use disturbance was recorded at a spring.

10.3.9 Human use plot

This bar plot shows the percentage of springs in each park where human use disturbance was observed.

10.3.10 Human use map

This map shows where human use disturbance was observed. If there is a field season slider at the top of the map, then data from a specific field season may be selected. If there is no field season slider, then data from the most recent field season are shown.

10.3.11 Livestock presence

This table lists visits where livestock disturbance was recorded at a spring.

10.3.12 Livestock plot

This bar plot shows the percentage of springs in each park where livestock disturbance was observed.

10.3.13 Livestock evidence map

This map shows where livestock disturbance was observed. If there is a field season slider at the top of the map, then data from a specific field season may be selected. If there is no field season slider, then data from the most recent field season are shown.

10.4 Wildlife evidence

10.4.1 Wildlife observed, no types recorded

This table lists visits where wildlife was observed, but no wildlife type was specified.

10.4.2 Wildlife type specified, no evidence recorded

This table lists visits where wildlife type was specified, but no evidence type was recorded.

10.4.3 Ungulates evidence

This table lists visits were evidence of ungulate (sheep or deer) activity was observed at a spring. Evidence can include direct observation, tracks, or scat.

10.4.4 Ungulates evidence map

This map shows where evidence of ungulate (sheep or deer) activity was observed. Evidence can include direction observation, tracks, or scat. If there is a field season slider at the top of the map, then data from a specific field season may be selected. If there is no field season slider, then data from the most recent field season are shown.

11 Usage Notes

A MOJN Desert Springs R Package that is designed to work with the datasets in their published format is available on GitHub. This package was used to implement the quality checks that are included in this report and to generate the three calculated datasets that are included in this data package.

12 Acknowledgements

The authors would like to acknowledge Geoff Moret, who developed the Desert Springs protocol.

13 References

14 Appendix A. Code Listing

# This setup code loads both reproducible reporting packages
# (delete those not needed) and packages for the actual project.
# Note that it also generates the start of a BibTex literature cited
# including the citations for R and all used packages

# reproducible reporting packages
RRpackages <- c('markdown',     # links to Sundown rendering library
                'rmarkdown',    # newer rendering via pandoc
                'pander',       # alternative renderer for markdown, plus better tables than just knitr
                'knitr',
                "dataMaid",     # for makeCodebooks
                "R.rsp",        # dynamic generation of scientific reports
                "kimisc",       #
                "papeR",        # stat tables
                "texreg",       # formatting regression results for LaTeX or html
                "rmdHelpers",   # misc from Mark Peterson thisFileName() thisFile_knit()
                'yaml',         # format data into markdown
                'rmdformats',   # templates including automatic ToC, also use_bookdown()
                'htmltools',    #
                "bibtex",
                "RefManageR",   # BibTeX reference manager
                "knitcitations",# For working with file paths
                "here"          # nice HTML widget tables
)

inst <- RRpackages %in% installed.packages()
if (length(RRpackages[!inst]) > 0) {
  install.packages(RRpackages[!inst], dep = TRUE, repos = "https://cloud.r-project.org")
}
lapply(RRpackages, library, character.only = TRUE)

# Now repeat for packages used in the analyses
pkgList <- c("devtools",        # tends to be needed/useful
             "RODBC",           # for connection to a database. 
             "EML",             # for data package creation and validation
             "kableExtra",      # added features for table formatting. 
             "english",         # converts numbers into english. Good for all that English stuff.
             "remotes",         # for install_github()
             "tidyverse",       # useful
             "formatR",
             "magrittr",
             "leaflet",
             "plotly",
             "svglite",
             "scales",
             "reactable",
             "slickR",
             "gridExtra",
             "glue",
             "DT")

inst <- pkgList %in% installed.packages()
if (length(pkgList[!inst]) > 0) {
  install.packages(pkgList[!inst], dep = TRUE, 
                   repos = "https://cloud.r-project.org")
}

lapply(pkgList, library, character.only = TRUE, quietly = TRUE)

if (! "EMLassemblyline" %in% installed.packages()) remotes::install_github("EDIorg/EMLassemblyline")
if (! "desertsprings" %in% installed.packages()) remotes::install_github("nationalparkservice/mojn-ds-rpackage")
require("EMLassemblyline")
require("desertsprings")

# create stub of citations for packages
pkgBibTex <- lapply(c("base", pkgList, RRpackages), citation)

# pkgBibTex <- do.call()

knitr::opts_chunk$set(
  root.dir = here::here(),
  echo = FALSE,
  comment = " ",
  dev = "svg",
  fig.path = here::here("figures"),
  tidy.opts = list(width.cutoff = 60),
  tidy = TRUE
)
# if ggplot, update theme to default to centered titles
if ("ggplot2" %in% .packages()) {
  theme_update(plot.title = element_text(hjust = 0.5))
}

knitr::opts_chunk$set(fig.width=8)

conn <- OpenDatabaseConnection()
# Write YAML parameters to file for consistent reuse across report and data packages
save(params,file=here::here("data", "temp", "reportParameters.RData"))

if (params$dataSource == "database") {
  conn <- tryCatch(OpenDatabaseConnection(),
                   error = {conn <- NA})
} else {
  conn <- NA
} 
raw_data_path <- here::here("data", "raw")


source(here::here("dataPackages", "DataPackageTemplate", "generate-metadata.R"))
# Load datasets for use

if (file.exists(file=here::here("data", "temp", "projectMetadata.RData"))) {
  load(file=here::here("data", "temp", "projectMetadata.RData"))
} else{
  projectMetadata<-list()
}

dat <- tibble::tibble(filepath = glue::as_glue(c(here::here("figures", "MOJA_P_BLA0014.JPG"),
                                                 here::here("figures", "JOTR_P_QUE0109.JPG"),
                                                 here::here("figures", "LAKE_P_SAL0019.JPG"),
                                                 here::here("figures", "PARA_P_DEA0145.JPG"),
                                                 here::here("figures", "MOJA_P_SIL0180.JPG"),
                                                 here::here("figures", "DEVA_P_COR0991.JPG"),
                                                 here::here("figures", "LAKE_P_GRA0058.JPG"),
                                                 here::here("figures", "DEVA_P_CUR0123.JPG"),
                                                 here::here("figures", "PARA_P_RAT0212.JPG"),
                                                 here::here("figures", "DEVA_P_GNO0081.JPG"),
                                                 here::here("figures", "DEVA_P_COT0794.JPG"),
                                                 here::here("figures", "JOTR_P_COT0294.JPG"))))

alt_text <- c("Small muddy puddle with trampling and disturbance by cattle",
              "Small pool situated in sand and bedrock at the bottom of a bouldery canyon",
              "Hot spring flowing over waterfall in canyon, dammed by sandbags at the bottom to form bathing pool",
              "Shallow, broad pond in grassy depression with pine trees in the background",
              "Shallow springbrook emerging from qanat, with active pipe diversion and cattle trampling in spring",
              "Small vegetated pool on very steep hillside with little other vegetation around",
              "Spring flowing over waterfall into large deep pool in narrow canyon, surrounded by lush riparian vegetation",
              "Small shallow pool emerges in the middle of desert shrubland, flows for a few meters downslope",
              "Water seeping out of contacts between bedrock layers, active pipe diversion of seepage",
              "Long narrow springbrook winds through a salt flat devoid of vegetation",
              "Shallow springbrook covered in aquatic plants in the shade of large cottonwood trees and other woody vegetation",
              "Dry spring in sandy wash below cluster of fan palms in bouldery canyon")

cap <- c("Black Diamond Spring, Mojave National Preserve",
         "Queen Mountain Spring, Joshua Tree National Park",
         "Salt Cedar Hot Spring, Lake Mead National Recreation Area",
         "Death Valley Pond, Parashant National Monument",
         "Silver Lead Qanat, Mojave National Preserve",
         "Corkscrew Spring, Death Valley National Park",
         "Grapevine Spring, Lake Mead National Recreation Area",
         "Currie Wells, Death Valley National Park",
         "Rattlesnake Spring, Parashant National Monument",
         "Gnome Spring, Death Valley National Park",
         "Cottonwood Spring, Death Valley National Park",
         "Cottonwood Palm Spring, Joshua Tree National Park")

imgs_alt <- mapply(function(x, txt){
  htmltools::tags$img(src = x, title = txt)
}, x = dat$filepath, txt = alt_text, SIMPLIFY = FALSE)

cP1 <- htmlwidgets::JS("function(slick,index) {
                            return '<a>'+(index+1)+'</a>';
                       }")

opts_dot_number <- slickR::settings(
    focusOnSelect = TRUE,
    dots = TRUE,
    customPaging = cP1
    )

slick_dots <- slickR::slickR(
  obj = imgs_alt,
  height = 400,
  width = "60%"
  )

slick_cap_synch <- slickR(obj = cap, slideType = "p") + settings(arrows = FALSE)

slick_dots %synch% slick_cap_synch + opts_dot_number 

locationmap <- LocationMap(conn)

locationmap

type <- c("Quantitative", "Quantitative", "Quantitative", "Qualitative", "Qualitative", "Qualitative", "Qualitative", "Qualitative")

msr <- c("Water Availability (Discrete)", "Water Availability (Continuous)", "Water Quality", "Dominant Vegetation", "Invasive Plants", "Wildlife Evidence", "Disturbance", "Repeat Photographs")

dscr <- c("Flow condition (presence or absence of surface water), discharge (flow rate of surface water), surface water dimensions (length and width of surface water)", "Record of wet / dry cycle (data-logging sensors)", "Core parameters (temperature, pH, specific conductance, and dissolved oxygen)", "Ranking of vegetation life forms within spring area", "List of invasive plants found within spring area", "List and description of wildlife evidence within spring area", "Index of natural and anthropogenic disturbances within spring area", "Visual documentation of spring source, upstream, and downstream")

tbl <- tibble::tibble(type, msr, dscr) %>%
  dplyr::rename(MeasurementType = type,
                Measurement = msr,
                Description = dscr)

if (params$isAccessible == "yes") {
  knitr::kable(tbl) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  tbl %>%
    reactable(compact = TRUE,
              striped = TRUE,
              columns = list(
                MeasurementType = colDef(width = 160),
                Description = colDef(minWidth = 180)))
}
FileName<-c("Column")
Description<-c("Description")
Table<-data.frame(FileName,Description)

reactable(Table,
          columns = list(FileName = colDef(name = "Filename")),
          pagination = FALSE,
          compact = TRUE, striped = TRUE)

notsampled <- qcNotSampled(conn = conn, data.source = "database")

notsampled %<>%
  select(-c(VisitType)) %>%
  arrange(SiteCode)

if (params$isAccessible == "yes") {
  knitr::kable(notsampled) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  reactable(dplyr::select(notsampled, -Notes),
            defaultColDef = colDef(minWidth = 50),
            searchable = TRUE,
            details = colDef(
              name = "Notes",
              details = function(index) {
                note <- notsampled$Notes[index]
                if (!is.na(note)) {
                  return(note)
                }
              }
            ),
            resizable = TRUE,
            compact = TRUE,
            striped = TRUE)
}
completeness <- qcCompleteness(conn = conn, data.source = "database")

completeness %<>%
  dplyr::mutate(Percent = round(Percent, 1)) %>%
  dplyr::filter(MonitoringStatus == "Sampled")

if (params$isAccessible == "yes") {
  knitr::kable(completeness) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  completeness %>%
    reactable(searchable = TRUE, compact = TRUE, striped = TRUE)
}
completeness.plot <- qcCompletenessPlot(conn = conn, data.source = "database")

if (params$isAccessible == "yes") {
  completeness.plot
} else {
  ggplotly(completeness.plot) %>%
    layout(
      legend = list(
        orientation = "h",
        y = -0.2,
        title = list(text = "Sample Status")
      )
    )  
}
visitdate <- qcVisitDate(conn = conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(visitdate) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  visitdate %>%
    reactable(searchable = TRUE, compact = TRUE, striped = TRUE)
}
visitdate.plots <- qcVisitDateTimelines(conn = conn, park = "DEVA", data.source = "database")

if (params$isAccessible == "yes") {
  visitdate.plots
} else {
  p <- ggplotly(visitdate.plots, tooltip = c("text"))
  
  for (i in 1:length(p$x$data)){
     if (!is.null(p$x$data[[i]]$name)){
         p$x$data[[i]]$name =  gsub("\\(","",str_split(p$x$data[[i]]$name,",")[[1]][1])
     }
   }
  p
}
visitdate.plots <- qcVisitDateTimelines(conn = conn, park = "JOTR", data.source = "database")

if (params$isAccessible == "yes") {
  visitdate.plots
} else {
  p <- ggplotly(visitdate.plots, tooltip = c("text"))
  
  for (i in 1:length(p$x$data)){
     if (!is.null(p$x$data[[i]]$name)){
         p$x$data[[i]]$name =  gsub("\\(","",str_split(p$x$data[[i]]$name,",")[[1]][1])
     }
   }
  p
}
visitdate.plots <- qcVisitDateTimelines(conn = conn, park = "LAKE", data.source = "database")

if (params$isAccessible == "yes") {
  visitdate.plots
} else {
  p <- ggplotly(visitdate.plots, tooltip = c("text"))
  
  for (i in 1:length(p$x$data)){
     if (!is.null(p$x$data[[i]]$name)){
         p$x$data[[i]]$name =  gsub("\\(","",str_split(p$x$data[[i]]$name,",")[[1]][1])
     }
   }
  p
}
visitdate.plots <- qcVisitDateTimelines(conn = conn, park = "MOJA", data.source = "database")

if (params$isAccessible == "yes") {
  visitdate.plots
} else {
  p <- ggplotly(visitdate.plots, tooltip = c("text"))
  
  for (i in 1:length(p$x$data)){
     if (!is.null(p$x$data[[i]]$name)){
         p$x$data[[i]]$name =  gsub("\\(","",str_split(p$x$data[[i]]$name,",")[[1]][1])
     }
   }
  p
}
visitdate.plots <- qcVisitDateTimelines(conn = conn, park = "PARA", data.source = "database")

if (params$isAccessible == "yes") {
  visitdate.plots
} else {
  p <- ggplotly(visitdate.plots, tooltip = c("text"))
  
  for (i in 1:length(p$x$data)){
     if (!is.null(p$x$data[[i]]$name)){
         p$x$data[[i]]$name =  gsub("\\(","",str_split(p$x$data[[i]]$name,",")[[1]][1])
     }
   }
  p
}
discrepancies <- qcSpringTypeDiscrepancies(conn = conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(discrepancies) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  discrepancies %>%
    reactable(searchable = TRUE, compact = TRUE, striped = TRUE)
}
dpl.check <- qcDPLCheck(conn, data.source = "database")

if (params$isAccessible == "yes") {
  dpl.check %>%
    rename_with(~ gsub(".DPL", "", .x)) %>%
    knitr::kable() %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  dpl.check %>%
    rename_with(~ gsub(".DPL", "", .x)) %>%
    reactable(searchable = TRUE, compact = TRUE, striped = TRUE)
}
summary <- qcSensorSummary(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(summary) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  summary %>%
    reactable(searchable = TRUE, compact = TRUE, striped = TRUE)
}
heatmap <- qcSensorHeatmap(conn, data.source = "database")

if (params$isAccessible == "yes") {
  heatmap
} else {
  ggplotly(heatmap, tooltip = "text")
}
problems <- qcSensorProblems(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(problems) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  reactable(dplyr::select(problems, -Notes),
            defaultColDef = colDef(minWidth = 50),
            searchable = TRUE,
            details = colDef(
              name = "Notes",
              details = function(index) {
                note <- problems$Notes[index]
                if (!is.na(note)) {
                  return(note)
                }
              }
            ),
            resizable = TRUE,
            compact = TRUE,
            striped = TRUE)
}
missing <- qcMissingSensors(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(missing) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  reactable(dplyr::select(missing, -Notes),
            defaultColDef = colDef(minWidth = 50),
            searchable = TRUE,
            details = colDef(
              name = "Notes",
              details = function(index) {
                note <- missing$Notes[index]
                if (!is.na(note)) {
                  return(note)
                }
              }
            ),
            resizable = TRUE,
            compact = TRUE,
            striped = TRUE)
}
downloads <- qcSensorDownloads(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(downloads) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  reactable(dplyr::select(downloads, -Notes),
            defaultColDef = colDef(minWidth = 50),
            searchable = TRUE,
            details = colDef(
              name = "Notes",
              details = function(index) {
                note <- downloads$Notes[index]
                if (!is.na(note)) {
                  return(note)
                }
              }
            ),
            resizable = TRUE,
            compact = TRUE,
            striped = TRUE)
}
dates <- qcSensorDates(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(dates) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  reactable(dplyr::select(dates, -Notes),
            defaultColDef = colDef(minWidth = 50),
            searchable = TRUE,
            details = colDef(
              name = "Notes",
              details = function(index) {
                note <- dates$Notes[index]
                if (!is.na(note)) {
                  return(note)
                }
              }
            ),
            resizable = TRUE,
            compact = TRUE,
            striped = TRUE)
}
springdischarge <- SpringDischarge(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(springdischarge) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  reactable(dplyr::select(springdischarge, -Notes),
            defaultColDef = colDef(minWidth = 50),
            searchable = TRUE,
            details = colDef(
              name = "Notes",
              details = function(index) {
                note <- springdischarge$Notes[index]
                if (!is.na(note)) {
                  return(note)
                }
              }
            ),
            resizable = TRUE,
            compact = TRUE,
            striped = TRUE)
}
drynonzero <- qcSpringDryWater(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(drynonzero) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  reactable(dplyr::select(drynonzero, -Notes),
            defaultColDef = colDef(minWidth = 50),
            searchable = TRUE,
            details = colDef(
              name = "Notes",
              details = function(index) {
                note <- drynonzero$Notes[index]
                if (!is.na(note)) {
                  return(note)
                }
              }
            ),
            resizable = TRUE,
            compact = TRUE,
            striped = TRUE)
}
notdrynowater <- qcSpringNotDryNoWater(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(notdrynowater) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  reactable(dplyr::select(notdrynowater, -Notes),
            defaultColDef = colDef(minWidth = 50),
            searchable = TRUE,
            details = colDef(
              name = "Notes",
              details = function(index) {
                note <- notdrynowater$Notes[index]
                if (!is.na(note)) {
                  return(note)
                }
              }
            ),
            resizable = TRUE,
            compact = TRUE,
            striped = TRUE)
}
dischargemissing <- qcDischargeMissing(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(dischargemissing) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  reactable(dplyr::select(dischargemissing, -Notes),
            defaultColDef = colDef(minWidth = 50),
            searchable = TRUE,
            details = colDef(
              name = "Notes",
              details = function(index) {
                note <- dischargemissing$Notes[index]
                if (!is.na(note)) {
                  return(note)
                }
              }
            ),
            resizable = TRUE,
            compact = TRUE,
            striped = TRUE)
}
volumetricmissing <- qcVolumetricMissing(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(volumetricmissing) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  volumetricmissing %>%
    reactable(searchable = TRUE, compact = TRUE, striped = TRUE)
}
fillevents <- qcVolumetricFillEvents(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(fillevents) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  fillevents %>%
    reactable(searchable = TRUE, compact = TRUE, striped = TRUE)
}
filltimes <- qcVolumetricTimes(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(filltimes) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  filltimes %>%
    reactable(searchable = TRUE, compact = TRUE, striped = TRUE)
}
continuouslength <- qcContinuousLength(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(continuouslength) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  continuouslength %>%
    reactable(searchable = TRUE, compact = TRUE, striped = TRUE)
}
flowcatcon <- FlowCategoriesContinuous(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(flowcatcon) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  flowcatcon %>%
    reactable(searchable = TRUE, compact = TRUE, striped = TRUE)
}
flowcatdiscon <- FlowCategoriesDiscontinuous(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(flowcatdiscon) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  flowcatdiscon %>%
    reactable(searchable = TRUE, compact = TRUE, striped = TRUE)
}
flowcatannualplot <- FlowCategoriesAnnualPlot(conn, data.source = "database")

if (params$isAccessible == "yes") {
  flowcatannualplot
} else {
  ggplotly(flowcatannualplot) %>%
    layout(
      legend = list(
        orientation = "h",
        y = -0.2,
        title = list(text = "Flow Category")
      )
    )
}
flowcat3yrplot <- FlowCategoriesThreeYearPlot(conn, data.source = "database")

if (params$isAccessible == "yes") {
  flowcat3yrplot
} else {
  ggplotly(flowcat3yrplot) %>%
    layout(
      legend = list(
        orientation = "h",
        y = -0.2,
        title = list(text = "Flow Category")
      )
    )
}
flowcatannualheatmap <- FlowCategoriesAnnualHeatMap(conn, park = "DEVA", data.source = "database")

if (params$isAccessible == "yes") {
  flowcatannualheatmap
} else {
  ggplotly(flowcatannualheatmap, tooltip = "text")
}
flowcatannualheatmap <- FlowCategoriesAnnualHeatMap(conn, park = "JOTR", data.source = "database")

if (params$isAccessible == "yes") {
  flowcatannualheatmap
} else {
  ggplotly(flowcatannualheatmap, tooltip = "text")
}
flowcatannualheatmap <- FlowCategoriesAnnualHeatMap(conn, park = "LAKE", data.source = "database")

if (params$isAccessible == "yes") {
  flowcatannualheatmap
} else {
  ggplotly(flowcatannualheatmap, tooltip = "text")
}
flowcatannualheatmap <- FlowCategoriesAnnualHeatMap(conn, park = "MOJA", data.source = "database")

if (params$isAccessible == "yes") {
  flowcatannualheatmap
} else {
  ggplotly(flowcatannualheatmap, tooltip = "text")
}
flowcatannualheatmap <- FlowCategoriesAnnualHeatMap(conn, park = "PARA", data.source = "database")

if (params$isAccessible == "yes") {
  flowcatannualheatmap
} else {
  ggplotly(flowcatannualheatmap, tooltip = "text")
}
flowcat3yrheatmap <- FlowCategoriesThreeYearHeatMap(conn, park = "DEVA", data.source = "database")

if (params$isAccessible == "yes") {
  flowcat3yrheatmap
} else {
  ggplotly(flowcat3yrheatmap, tooltip = "text")
}
flowcat3yrheatmap <- FlowCategoriesThreeYearHeatMap(conn, park = "JOTR", data.source = "database")

if (params$isAccessible == "yes") {
  flowcat3yrheatmap
} else {
  ggplotly(flowcat3yrheatmap, tooltip = "text")
}
flowcat3yrheatmap <- FlowCategoriesThreeYearHeatMap(conn, park = "LAKE", data.source = "database")

if (params$isAccessible == "yes") {
  flowcat3yrheatmap
} else {
  ggplotly(flowcat3yrheatmap, tooltip = "text")
}
flowcat3yrheatmap <- FlowCategoriesThreeYearHeatMap(conn, park = "MOJA", data.source = "database")

if (params$isAccessible == "yes") {
  flowcat3yrheatmap
} else {
  ggplotly(flowcat3yrheatmap, tooltip = "text")
}
flowcat3yrheatmap <- FlowCategoriesThreeYearHeatMap(conn, park = "PARA", data.source = "database")

if (params$isAccessible == "yes") {
  flowcat3yrheatmap
} else {
  ggplotly(flowcat3yrheatmap, tooltip = "text")
}
if (params$isAccessible == "yes") {
  flowcatmap_access <- FlowCategoriesMap(conn, interactive = "no", data.source = "database")
  flowcatmap_access
} else {
  flowcatmap_interactive <- FlowCategoriesMap(conn, interactive = "yes", data.source = "database")
  flowcatmap_interactive
}
wqsanity <- qcWqSanity(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(wqsanity) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  wqsanity %>%
    reactable(searchable = TRUE, compact = TRUE, striped = TRUE)
}
wqflags <- qcWqFlags(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(wqflags) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  reactable(dplyr::select(wqflags, -FlagNote),
            defaultColDef = colDef(minWidth = 50),
            searchable = TRUE,
            details = colDef(
              name = "Notes",
              details = function(index) {
                note <- wqflags$FlagNote[index]
                if (!is.na(note)) {
                  return(note)
                }
              }
            ),
            resizable = TRUE,
            compact = TRUE,
            striped = TRUE)
}
wqstats <- WqStats(conn, data.source = "database")

wqstats %<>% dplyr::filter(Park != "CAMO")

if (params$isAccessible == "yes") {
  knitr::kable(wqstats) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  wqstats %>%
    reactable(searchable = TRUE, compact = TRUE, striped = TRUE)
}
tempplot <- WqPlotTemp(conn, data.source = "database")

if (params$isAccessible == "yes") {
    tempplot
} else {
    ggplotly(tempplot)
}
phplot <- WqPlotPH(conn, data.source = "database")

if (params$isAccessible == "yes") {
    phplot
} else {
    ggplotly(phplot)
}
spcondplot <- WqPlotSpCond(conn, data.source = "database")

if (params$isAccessible == "yes") {
    spcondplot
} else {
    ggplotly(spcondplot)
}
tempplot <- WqPlotDOmgL(conn, data.source = "database")

if (params$isAccessible == "yes") {
    tempplot
} else {
    ggplotly(tempplot)
}
vegnolife <- qcVegPresentNoLifeforms(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(vegnolife) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  vegnolife %>%
    reactable(searchable = TRUE, compact = TRUE, striped = TRUE)
}
noveglife <- qcNoVegLifeformsPresent(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(noveglife) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  noveglife %>%
    reactable(searchable = TRUE, compact = TRUE, striped = TRUE)
}
lifenorank <- qcLifeformPresentNoRank(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(lifenorank) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  lifenorank %>%
    reactable(searchable = TRUE, compact = TRUE, striped = TRUE)
}
rankcheck <- qcLifeformRankCheck(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(rankcheck) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  rankcheck %>%
    reactable(searchable = TRUE, compact = TRUE, striped = TRUE)
}
lifeformobservations <- LifeformsPresence(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(lifeformobservations) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  lifeformobservations %>%
    reactable(searchable = TRUE, compact = TRUE, striped = TRUE)
}
lifeformsperspringplot <- LifeformsPerSpringPlot(conn, data.source = "database")

if (params$isAccessible == "yes") {
  lifeformsperspringplot
} else {
  ggplotly(lifeformsperspringplot, tooltip = "text")  
}
lifeformsobservationsplot <- MostCommonLifeformsPlot(conn, data.source = "database")

if (params$isAccessible == "yes") {
  lifeformsobservationsplot
} else {
  ggplotly(lifeformsobservationsplot, tooltip = "text")
}
invplants <- InvasivePlants(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(invplants) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  reactable(dplyr::select(invplants, -Notes),
            defaultColDef = colDef(minWidth = 50),
            searchable = TRUE,
            details = colDef(
              name = "Notes",
              details = function(index) {
                note <- invplants$Notes[index]
                if (!is.na(note)) {
                  return(note)
                }
              }
            ),
            resizable = TRUE,
            compact = TRUE,
            striped = TRUE)
}
invplantmap <- InvasivePlantsMap(conn, data.source = "database")

invplantmap
overalldisturbance <- qcOverallDisturbance(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(overalldisturbance) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  overalldisturbance %>%
    reactable(searchable = TRUE, compact = TRUE, striped = TRUE)
}
flowmodnohuman <- qcFlowModNoHuman(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(flowmodnohuman) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  flowmodnohuman %>%
    reactable(searchable = TRUE, compact = TRUE, striped = TRUE)
}
flowmoddiscrepancies <- qcFlowModDiscrepancies(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(flowmoddiscrepancies) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  flowmoddiscrepancies %>%
    reactable(searchable = TRUE, compact = TRUE, striped = TRUE)
}
flowmodstatus <- FlowModStatus(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(flowmodstatus) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  flowmodstatus %>%
    reactable(searchable = TRUE, compact = TRUE, striped = TRUE)
}
flowmodcount <- FlowModCount(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(flowmodcount) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  flowmodcount %>%
    reactable(searchable = TRUE, compact = TRUE, striped = TRUE)
}
flowmodplot <- FlowModPlot(conn, data.source = "database")

if (params$isAccessible == "yes") {
  flowmodplot
} else {
  ggplotly(flowmodplot)
}
disturbcount <- DisturbanceCount(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(disturbcount) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  disturbcount %>%
    reactable(searchable = TRUE, compact = TRUE, striped = TRUE)
}
humanuseobs <- HumanUseObservations(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(humanuseobs) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  reactable(dplyr::select(humanuseobs, -Notes),
            defaultColDef = colDef(minWidth = 50),
            searchable = TRUE,
            details = colDef(
              name = "Notes",
              details = function(index) {
                note <- humanuseobs$Notes[index]
                if (!is.na(note)) {
                  return(note)
                }
              }
            ),
            resizable = TRUE,
            compact = TRUE,
            striped = TRUE)
}
humanuseplot <- HumanUsePlot(conn, data.source = "database")

if (params$isAccessible == "yes") {
  humanuseplot
} else {
  ggplotly(humanuseplot)
}
humanusemap <- HumanUseMap(conn, data.source = "database")

humanusemap
livestockobs <- LivestockObservations(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(livestockobs) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  reactable(dplyr::select(livestockobs, -Notes),
            defaultColDef = colDef(minWidth = 50),
            searchable = TRUE,
            details = colDef(
              name = "Notes",
              details = function(index) {
                note <- livestockobs$Notes[index]
                if (!is.na(note)) {
                  return(note)
                }
              }
            ),
            resizable = TRUE,
            compact = TRUE,
            striped = TRUE)
}
livestockplot <- LivestockPlot(conn, data.source = "database")

if (params$isAccessible == "yes") {
  livestockplot
} else {
  ggplotly(livestockplot)
}
livestockmap <- LivestockMap(conn, data.source = "database")

livestockmap
wildlifenotype <- qcWildlifeObservedNoTypes(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(wildlifenotype) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  reactable(dplyr::select(wildlifenotype, -Notes),
            defaultColDef = colDef(minWidth = 50),
            searchable = TRUE,
            details = colDef(
              name = "Notes",
              details = function(index) {
                note <- wildlifenotype$Notes[index]
                if (!is.na(note)) {
                  return(note)
                }
              }
            ),
            resizable = TRUE,
            compact = TRUE,
            striped = TRUE)
}
wildlifenoevidence <- qcWildlifeObservedNoEvidence(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(wildlifenoevidence) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  reactable(dplyr::select(wildlifenoevidence, -Notes),
            defaultColDef = colDef(minWidth = 50),
            searchable = TRUE,
            details = colDef(
              name = "Notes",
              details = function(index) {
                note <- wildlifenoevidence$Notes[index]
                if (!is.na(note)) {
                  return(note)
                }
              }
            ),
            resizable = TRUE,
            compact = TRUE,
            striped = TRUE)
}
ungulatesevidence <- UngulatesEvidence(conn, data.source = "database")

if (params$isAccessible == "yes") {
  knitr::kable(ungulatesevidence) %>%
    kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "left")
} else {
  reactable(dplyr::select(ungulatesevidence, -Notes),
            defaultColDef = colDef(minWidth = 50),
            searchable = TRUE,
            details = colDef(
              name = "Notes",
              details = function(index) {
                note <- ungulatesevidence$Notes[index]
                if (!is.na(note)) {
                  return(note)
                }
              }
            ),
            resizable = TRUE,
            compact = TRUE,
            striped = TRUE)
}
ungulatesmap <- UngulatesMap(conn, data.source = "database")

ungulatesmap
sessionInfo()
Sys.time()
# if (params$dataSource == "database") {
#    CloseDatabaseConnection(conn)
# }

CloseDatabaseConnection(conn)

15 Appendix B. Session and Version Information

  R version 4.1.2 (2021-11-01)
  Platform: x86_64-w64-mingw32/x64 (64-bit)
  Running under: Windows 10 x64 (build 19042)
  
  Matrix products: default
  
  locale:
  [1] LC_COLLATE=English_United States.1252 
  [2] LC_CTYPE=English_United States.1252   
  [3] LC_MONETARY=English_United States.1252
  [4] LC_NUMERIC=C                          
  [5] LC_TIME=English_United States.1252    
  
  attached base packages:
  [1] stats     graphics  grDevices utils     datasets  methods   base     
  
  other attached packages:
   [1] desertsprings_0.1.0   EMLassemblyline_3.5.4 DT_0.25              
   [4] glue_1.6.2            gridExtra_2.3         slickR_0.5.0         
   [7] reactable_0.3.0       scales_1.2.0          svglite_2.1.0        
  [10] plotly_4.10.0         leaflet_2.1.1         magrittr_2.0.3       
  [13] formatR_1.12          forcats_0.5.1         stringr_1.4.0        
  [16] purrr_0.3.4           readr_2.1.2           tidyr_1.2.0          
  [19] tibble_3.1.7          ggplot2_3.3.6         tidyverse_1.3.1      
  [22] remotes_2.4.2         english_1.2-6         kableExtra_1.3.4     
  [25] EML_2.0.6.1           RODBC_1.3-19          devtools_2.4.3       
  [28] usethis_2.1.6         here_1.0.1            knitcitations_1.0.12 
  [31] RefManageR_1.3.0      bibtex_0.4.2.3        htmltools_0.5.2      
  [34] rmdformats_1.0.4      yaml_2.3.5            rmdHelpers_1.2       
  [37] dplyr_1.0.9           texreg_1.38.6         papeR_1.0-5          
  [40] xtable_1.8-4          car_3.1-0             carData_3.0-5        
  [43] kimisc_0.4            R.rsp_0.45.0          dataMaid_1.4.1       
  [46] knitr_1.39            pander_0.6.5          rmarkdown_2.14       
  [49] markdown_1.1         
  
  loaded via a namespace (and not attached):
    [1] readxl_1.4.0       uuid_1.1-0         backports_1.4.1   
    [4] tidytext_0.3.4     systemfonts_1.0.4  plyr_1.8.7        
    [7] lazyeval_0.2.2     jqr_1.2.3          crosstalk_1.2.0   
   [10] SnowballC_0.7.0    odbc_1.3.3         digest_0.6.29     
   [13] gdata_2.18.0.1     fansi_1.0.3        memoise_2.0.1     
   [16] emld_0.5.1         tzdb_0.3.0         modelr_0.1.8      
   [19] gmodels_2.18.1.1   R.utils_2.12.0     vroom_1.5.7       
   [22] prettyunits_1.1.1  colorspace_2.0-3   blob_1.2.3        
   [25] rvest_1.0.2        haven_2.5.0        xfun_0.31         
   [28] callr_3.7.1        crayon_1.5.1       jsonlite_1.8.0    
   [31] gtable_0.3.0       webshot_0.5.3      V8_4.2.1          
   [34] R.cache_0.16.0     pkgbuild_1.3.1     DEoptimR_1.0-11   
   [37] abind_1.4-5        pool_0.1.6         DBI_1.1.3         
   [40] Rcpp_1.0.8.3       viridisLite_0.4.0  bit_4.0.4         
   [43] htmlwidgets_1.5.4  httr_1.4.3         RColorBrewer_1.1-3
   [46] ellipsis_0.3.2     farver_2.1.1       pkgconfig_2.0.3   
   [49] R.methodsS3_1.8.2  sass_0.4.2         dbplyr_2.2.1      
   [52] utf8_1.2.2         labeling_0.4.2     later_1.3.0       
   [55] tidyselect_1.1.2   rlang_1.0.3        reactR_0.4.4      
   [58] munsell_0.5.0      cellranger_1.1.0   tools_4.1.2       
   [61] cachem_1.0.6       cli_3.3.0          generics_0.1.3    
   [64] broom_1.0.0        evaluate_0.15      fastmap_1.1.0     
   [67] processx_3.6.1     bit64_4.0.5        fs_1.5.2          
   [70] jsonld_2.2         robustbase_0.95-0  mime_0.12         
   [73] R.oo_1.25.0        xml2_1.3.3         tokenizers_0.2.1  
   [76] compiler_4.1.2     rstudioapi_0.13    curl_4.3.2        
   [79] reprex_2.0.1       bslib_0.4.0        stringi_1.7.6     
   [82] ps_1.7.1           lattice_0.20-45    Matrix_1.5-1      
   [85] vctrs_0.4.1        pillar_1.8.0       lifecycle_1.0.1   
   [88] jquerylib_0.1.4    data.table_1.14.2  httpuv_1.6.6      
   [91] R6_2.5.1           promises_1.2.0.1   bookdown_0.29     
   [94] janeaustenr_1.0.0  sessioninfo_1.2.2  MASS_7.3-54       
   [97] gtools_3.9.3       assertthat_0.2.1   pkgload_1.3.0     
  [100] rprojroot_2.0.3    withr_2.5.0        parallel_4.1.2    
  [103] hms_1.1.1          grid_4.1.2         shiny_1.7.2       
  [106] lubridate_1.8.0    base64enc_0.1-3
  [1] "2022-09-21 12:37:58 PDT"
 

The National Park Service, Natural Resource Stewardship and Science office in Fort Collins, Colorado, publishes a range of reports that address natural resource topics. These reports are of interest and applicability to a broad audience in the National Park Service and others in natural resource management, including scientists, conservation and environmental constituencies, and the public.

Data Release Reports are created by the National Park Service and provide detailed descriptions of valuable research datasets, including the methods used to collect and process the data, technical analyses to evaluate the quality of those data, and information to guide use of those data. Data Release Reports focus on helping others reuse data, rather than presenting results, testing hypotheses, or presenting new interpretations, methods or in-depth analyses.

All manuscripts in the series receive the appropriate level of peer review to ensure that the information is scientifically credible, technically accurate, appropriately written for the intended audience, and designed and published in a professional manner.

The creation of data sets documented in this report followed peer reviewed methods described or referenced herein. Resultant data sets were reviewed to ensure accuracy, completeness, and consistency with documented data quality standards, as well as for usability and reproducibility.

Views, statements, findings, conclusions, recommendations, and data in this report do not necessarily reflect views and policies of the National Park Service, U.S. Department of the Interior. Mention of trade names or commercial products does not constitute endorsement or recommendation for use by the U.S. Government.

This report is available in digital format from the National Park Service Data Store.